Background steps execution engine#4476
Conversation
955429e to
13594a5
Compare
becd2c1 to
b88b481
Compare
4fbc66b to
95ab648
Compare
b88b481 to
5007995
Compare
95ab648 to
5a09827
Compare
5a09827 to
265b98d
Compare
265b98d to
c89cec5
Compare
50a2671 to
369bbe9
Compare
80ebb21 to
b30b777
Compare
48753dd to
40c6e91
Compare
40c6e91 to
200d050
Compare
d6aa1f6 to
975ab20
Compare
975ab20 to
c6e2142
Compare
| public void InitializeCoordinator(int maxConcurrent) | ||
| { | ||
| _backgroundSteps.Clear(); | ||
| _waitedStepIds.Clear(); |
There was a problem hiding this comment.
is _waitedStepIds track the completed steps or uncompleted steps?
| private async Task<TaskResult> WaitForStepsAsync(string[] stepIds, CancellationToken cancellationToken) | ||
| { | ||
| var ids = stepIds ?? Array.Empty<string>(); | ||
| await WaitForStepTasksAsync(ids, cancellationToken); | ||
| return CompleteWaitedSteps(ids); | ||
| } | ||
|
|
||
| private async Task<TaskResult> WaitForAllAsync(CancellationToken cancellationToken) | ||
| { | ||
| var remaining = _backgroundSteps.Keys.Where(id => !_waitedStepIds.Contains(id)).ToList(); | ||
| await WaitForStepTasksAsync(remaining, cancellationToken); | ||
| return CompleteWaitedSteps(remaining); | ||
| } |
There was a problem hiding this comment.
do we still want to have 2 extra private methods? it feels like we can just inline the code to where it used?
| Trace.Info($"Background step '{stepId}' queued (slot will be acquired asynchronously)."); | ||
| } | ||
|
|
||
| private async Task ExecuteBackgroundStepCoreAsync( |
There was a problem hiding this comment.
we can probably move all private method under the Private helpers section down.
|
|
||
| private async Task CancelStepsAsync(string[] cancelStepIds) | ||
| { | ||
| if (cancelStepIds == null || cancelStepIds.Length == 0) return; |
There was a problem hiding this comment.
| if (cancelStepIds == null || cancelStepIds.Length == 0) return; | |
| if (cancelStepIds == null || cancelStepIds.Length == 0) | |
| { | |
| return; | |
| } |
|
|
||
| foreach (var id in cancelStepIds) | ||
| { | ||
| FlushDeferredState(id); |
There was a problem hiding this comment.
should we call CompleteWaitedSteps() instead?
| if (step.ExecutionContext.Result == TaskResult.Failed) | ||
| { | ||
| Trace.Info($"Propagating failure from background step '{step.ExecutionContext.ContextName}' to job result."); | ||
| jobContext.Result = TaskResultUtil.MergeTaskResults(jobContext.Result, TaskResult.Failed); |
There was a problem hiding this comment.
can we defer the caller in steprunner to update jobcontext.result?
| /// </summary> | ||
| public async Task RunControlFlowAsync(IExecutionContext stepContext, object data) | ||
| { | ||
| var controlFlow = data as ControlFlowStepData; |
There was a problem hiding this comment.
do we need to print anything to the output, so customer can know what we are doing under the step?
| } | ||
| catch (TimeoutException) | ||
| { | ||
| Trace.Info($"Some background steps did not terminate within {graceSeconds}s grace period."); |
There was a problem hiding this comment.
should we mark all steps that haven't finish as canceled?
| { | ||
| FlushDeferredState(id); | ||
| _waitedStepIds.Add(id); | ||
| if (_backgroundSteps.TryGetValue(id, out var entry) && entry.Step.ExecutionContext.Result == TaskResult.Failed) |
There was a problem hiding this comment.
why we only care about TaskResult.Failed? what about canceled, etc.
| /// Pure data for control-flow steps (wait, wait-all, cancel). | ||
| /// Type uses Pipelines.BackgroundControlTypes string constants. | ||
| /// </summary> | ||
| public sealed class ControlFlowStepData |
There was a problem hiding this comment.
should we rename this BackgroundStepControlFlowData?
Summary
Adds the core background step execution engine to the runner, enabling concurrent step execution within a single job. Background steps (
background: true) run asynchronously while the foreground step loop continues. Control-flow steps (wait,wait-all,cancel) coordinate synchronization and lifecycle management.Changes
New files
BackgroundStepCoordinator.cs— Coordinates background step execution, waiting, cancellation, and deferred state flushing. Manages a concurrency semaphore (default max 10).ControlFlowStepData.cs— Data class for wait/wait-all/cancel steps.BackgroundStepsL0.cs— Unit tests for background step coordination, waiting, cancellation, and failure propagation.Modified files
StepsRunner.cs— IntegratesBackgroundStepCoordinator; background steps are queued without blocking the main loop; control-flow steps are dispatched directly; implicit wait-all runs before post-hooks as a safety net.JobExtension.cs— Initializes control-flow steps fromBackgroundStepControlpipeline type; maps logical step IDs to external IDs for UI reference; creates implicit wait-all for uncovered background steps.ExecutionContext.cs— Adds background step metadata as optional parameters toCreateChildKey design decisions